package com.misalen.doc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.misalen.annotations.ControllerApi;
import org.misalen.annotations.Enumeration;
import org.misalen.annotations.InterfaceApi;
import org.misalen.annotations.ModelComment;
import org.misalen.validate.annotation.Email;
import org.misalen.validate.annotation.Length;
import org.misalen.validate.annotation.MobilePhone;
import org.misalen.validate.annotation.Required;
import org.misalen.web.database.model.mysql.BaseSqlModel;
import org.misalen.web.database.model.mysql.sys.SysWordbook;
import org.misalen.web.database.model.mysql.sys.SysWordbookItem;
import org.misalen.web.database.repository.mysql.SysWordbookItemRepository;
import org.misalen.web.database.repository.mysql.SysWordbookRepository;
import org.misalen.web.database.util.PaginationFrom;
import org.misalen.web.server.forms.BasePageForm;
import org.springframework.web.bind.annotation.RequestMapping;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.misalen.doc.model.DocClassify;
import com.misalen.doc.model.DocInterface;
import com.misalen.doc.model.DocParameter;

public class AnnotationUtil {
	private static String[] letter = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
			"q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };

	private static SysWordbookRepository sysWordbookRepository;
	private static SysWordbookItemRepository sysWordbookItemRepository;

	/**
	 * 获取接口说明
	 * 
	 * @param packageName
	 *            包名
	 * @param filterFile
	 *            过滤文件
	 * @param sysWordbookItemRepository
	 * @param sysWordbookRepository
	 * @param index
	 *            序号
	 * @return List<DocClassify>
	 * @throws Exception
	 */
	public static List<DocClassify> getDocClassifies(String packageName, HashSet<String> filterFile,
			SysWordbookRepository sysWordbookRepository, SysWordbookItemRepository sysWordbookItemRepository)
			throws Exception {
		AnnotationUtil.sysWordbookRepository = sysWordbookRepository;
		AnnotationUtil.sysWordbookItemRepository = sysWordbookItemRepository;
		List<DocClassify> classifies = new ArrayList<>();
		List<Class<?>> classes = ClassUtil.getClasses(packageName);
		// 遍历包下所有的类
		for (Class<?> clas : classes) {
			if (filterFile.contains(clas.getSimpleName())) {
				continue;
			}
			DocClassify classify = getDocClassify(clas, letter);
			if (classify != null) {
				classifies.add(classify);
			}
		}
		Collections.sort(classifies, new Comparator<DocClassify>() {
			public int compare(DocClassify list1, DocClassify list2) {
				return list1.index - list2.index;
			}
		});
		return classifies;
	}

	/**
	 * 获取一个类中的接口
	 * 
	 * @param clas
	 * @param index
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	public static DocClassify getDocClassify(Class<?> clas, String[] index) throws Exception {
		// 获取 Controller类的描述文档
		ControllerApi controllerApi = clas.getAnnotation(ControllerApi.class);
		// 获取 Controller类的地址描述
		RequestMapping requestMapping = clas.getAnnotation(RequestMapping.class);
		if (controllerApi != null && requestMapping != null) {
			System.out.println("处理类：" + clas.getSimpleName() + "(" + controllerApi.value() + ")");
			DocClassify classify = new DocClassify();
			classify.name = controllerApi.value();
			classify.index = controllerApi.index();
			classify.explain = controllerApi.description();
			for (Method field : clas.getMethods()) {
				DocInterface docInterface = getDocInterface(field, requestMapping.value()[0]);
				if (docInterface != null) {
					docInterface.letter = index[classify.docInterfaces.size()];
					classify.docInterfaces.add(docInterface);
				}
			}
			return classify;
		}
		return null;
	}

	/**
	 * 获取接口中的信息
	 * 
	 * @param field
	 * @param baseUrl
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	private static DocInterface getDocInterface(Method method, String baseUrl) throws Exception {
		// 获取 接口描述文档
		InterfaceApi interfaceApi = method.getAnnotation(InterfaceApi.class);
		// 废弃接口
		Deprecated deprecated = method.getAnnotation(Deprecated.class);
		if (interfaceApi != null && deprecated == null) {
			DocInterface docInterface = new DocInterface();
			List<DocParameter> docParameters = getDocResultParameters(interfaceApi);
			docInterface.docReturnParameters.addAll(docParameters);
			// 获取 接口地址
			RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
			if (requestMapping != null) {
				System.out.println("处理方法：" + interfaceApi.value());
				docInterface.name = interfaceApi.value();
				docInterface.url = baseUrl + requestMapping.value()[0];
				docInterface.explain = interfaceApi.description();
				Class<?>[] parameters = method.getParameterTypes();
				for (Class<?> parameter : parameters) {// 接口请求参数
					List<DocParameter> docParameter = getDocRequestParameters(parameter);
					if (docParameter != null) {
						docInterface.docRequestParameters.addAll(docParameter);
					}

				}
				return docInterface;
			}
		}
		return null;
	}

	/**
	 * 获取 返回参数
	 * 
	 * @param interfaceApi
	 * 
	 * @return
	 */
	private static List<DocParameter> getDocResultParameters(InterfaceApi interfaceApi) throws Exception {
		List<DocParameter> docParameters = new ArrayList<>();
 		if (interfaceApi.resultClass().length > 0) {
			docParameters.addAll(getDocResultChildrenParameters(interfaceApi.resultClass()[0], interfaceApi));
			for (int i = 0; i < interfaceApi.resultClass().length; i++) {
				Class<?> class1 = interfaceApi.resultClass()[i];
				if (class1 == PaginationFrom.class && interfaceApi.resultClass().length >= i + 1) {
					for (DocParameter docParameter : docParameters) {
						if (docParameter.type.equals("List")) {
							docParameter.children.addAll(
									getDocResultChildrenParameters(interfaceApi.resultClass()[i + 1], interfaceApi));
						}
					}
				}
			}
		}
		return docParameters;
	}

	/**
	 * 获取 详细返回参数
	 * 
	 * @param interfaceApi
	 * 
	 * @param interfaceApi
	 * 
	 * @return
	 */
	private static List<DocParameter> getDocResultChildrenParameters(Class<?> docClassify, InterfaceApi interfaceApi)
			throws Exception {
		List<DocParameter> docParameters = new ArrayList<>();
		Set<String> include = new HashSet<String>();
		Set<String> filter = new HashSet<String>();
		if (docClassify != PaginationFrom.class) {
			for (String string : interfaceApi.include()) {
				include.add(string);
			}
			for (String string : interfaceApi.filter()) {
				filter.add(string);
			}
			JsonIgnoreProperties ignoreProperties = docClassify.getAnnotation(JsonIgnoreProperties.class);
			if (filter.size() == 0 && ignoreProperties != null) {
				for (String string : ignoreProperties.value()) {
					filter.add(string);
				}
			}
		}

		if (docClassify.getSuperclass() == BaseSqlModel.class) {
			for (Field field2 : docClassify.getSuperclass().getDeclaredFields()) {
				boolean isPass = false;
				if (include.size() > 0) {
					if (include.contains(field2.getName())) {
						isPass = false;
					} else {
						isPass = true;
					}
				} else if (filter.size() > 0) {
					if (filter.contains(field2.getName())) {
						isPass = true;
					} else {
						isPass = false;
					}
				}
				ModelComment comment = field2.getAnnotation(ModelComment.class);
				if (!isPass && comment != null) {
					DocParameter docParameter = new DocParameter();
					docParameter.explain = comment.value();
					docParameter.name = field2.getName();
					docParameter.type = field2.getType().getSimpleName();
					docParameters.add(docParameter);
				}
			}
		}
		for (Field field : docClassify.getDeclaredFields()) {
			boolean isPass = false;
			if (include.size() > 0) {
				if (include.contains(field.getName())) {
					isPass = false;
				} else {
					isPass = true;
				}
			} else if (filter.size() > 0) {
				if (filter.contains(field.getName())) {
					isPass = true;
				} else {
					isPass = false;
				}
			}
			ModelComment comment = field.getAnnotation(ModelComment.class);
			if (!isPass && comment != null) {
				DocParameter docParameter = new DocParameter();
				docParameter.explain = comment.value();
				docParameter.name = field.getName();
				docParameter.type = field.getType().getSimpleName();
				docParameters.add(docParameter);
				typeCast(field, docParameters, interfaceApi);
			}
		}
		return docParameters;
	}

	private static void typeCast(Field field, List<DocParameter> docParameters, InterfaceApi interfaceApi)
			throws Exception {
		if (ClassUtil.isWrapClass(field.getType())) {
			return;
		} else if (field.getType().isAssignableFrom(List.class)) {
			Type genType = field.getGenericType();
			if (!(genType instanceof ParameterizedType)) {
				return;
			}
			Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
			if (!(params[0] instanceof Class)) {
				return;
			}
			docParameters.get(docParameters.size() - 1).children
					.addAll(getDocResultChildrenParameters((Class<?>) params[0], interfaceApi));
		} else if (ClassUtil.isInternalClass(field.getType())) {
			docParameters.get(docParameters.size() - 1).type = Object.class.getSimpleName();
			docParameters.get(docParameters.size() - 1).children
					.addAll(getDocResultChildrenParameters(field.getType(), interfaceApi));
		}

	}

	/**
	 * 获取请求接口参数
	 * 
	 * @param parameter
	 * @return
	 * @throws Exception
	 */
	private static List<DocParameter> getDocRequestParameters(Class<?> class1) throws Exception {
		List<DocParameter> docParameters = new ArrayList<>();
		if(class1.getSuperclass()==BasePageForm.class){
			docParameters.addAll(getDocRequestParameters(class1.getSuperclass()));
		}
		for (Field field : class1.getDeclaredFields()) {
			ModelComment modelComment = field.getAnnotation(ModelComment.class);
			if (modelComment != null) {
				DocParameter docParameter = new DocParameter();
				docParameter.explain = modelComment.value();
				docParameter.name = field.getName();
				docParameter.type = field.getType().getSimpleName();
				Enumeration enumeration = field.getAnnotation(Enumeration.class);
				if (enumeration != null) {
					SysWordbook sysWordbook = sysWordbookRepository.getByCode(enumeration.value());
					if (sysWordbook != null) {
						List<SysWordbookItem> items = sysWordbookItemRepository.findByWbId(sysWordbook.getPrimaryKey());
						StringBuffer buffer = new StringBuffer();
						buffer.append("{");
						for (SysWordbookItem sysWordbookItem : items) {
							buffer.append(sysWordbookItem.getItemCode());
							buffer.append("(");
							buffer.append(sysWordbookItem.getItemName());
							buffer.append("),");
						}
						buffer.append("}");
						docParameter.explain = modelComment.value() + buffer.toString();
					}
				}
				if (field.getAnnotation(Required.class) != null) {
					docParameter.required = "Y";
				} else if (field.getAnnotation(Email.class) != null) {
					docParameter.required = "Y(邮箱)";
				} else if (field.getAnnotation(MobilePhone.class) != null) {
					docParameter.required = "Y(手机号)";
				} else if (field.getAnnotation(Length.class) != null) {
					if (field.getAnnotation(Length.class).length() != -1) {
						docParameter.required = "Y(规定长度：" + field.getAnnotation(Length.class).length() + ")";
					} else {
						docParameter.required = "Y(规定长度：" + field.getAnnotation(Length.class).min() + "-"
								+ field.getAnnotation(Length.class).max() + ")";
					}
				} else {
					docParameter.required = "N";
				}
				docParameters.add(docParameter);

				if (ClassUtil.isWrapClass(field.getType())) {
					continue;
				} else if (field.getType().isAssignableFrom(List.class)) {
					Type genType = field.getGenericType();
					if (!(genType instanceof ParameterizedType)) {
						continue;
					}
					Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
					if (!(params[0] instanceof Class)) {
						continue;
					}
					docParameters.get(docParameters.size() - 1).children
							.addAll(getDocRequestParameters((Class<?>) params[0]));
				} else if (ClassUtil.isInternalClass(field.getType())) {
					docParameters.get(docParameters.size() - 1).type = Object.class.getSimpleName();
					docParameters.get(docParameters.size() - 1).children
							.addAll(getDocRequestParameters(field.getType()));
				}
			}
		}
		return docParameters;
	}
}
